#ifndef xpack_interface_can_callback
#define xpack_interface_can_callback
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::interface_can_callback::inc
#include"macro/xis_nullptr.hpp"
#include"macro/xexport.hpp"
#include"macro/xmodify.hpp"
#include"macro/xnew.hpp"
#include"meta/is_same.hpp"
#include"meta_seq/vnull.hpp"
#pragma pop_macro("xuser")

namespace mixc::interface_can_callback{
    template<class functor_t, class ret_t, class ... args_t>
    concept can_callback = requires(functor_t lambda, ret_t * ret, args_t ... args){
        xnew(ret) ret_t(lambda(args...));
    };

    template<class functor_t, class ... args_t>
    concept can_callback_without_return_value = requires(functor_t lambda, args_t ... args){
        lambda(args...);
    };

    template<class, class> struct meta{ 
        static constexpr bool result = false;
    };

    template<class functor_t, class ret_t, class ... args_t>
    struct meta<functor_t, ret_t(args_t...)>{
        static constexpr bool result = can_callback<functor_t, ret_t, args_t...>;
    };

    template<class functor_t, class ... args_t>
    struct meta<functor_t, void(args_t...)>{
        static constexpr bool result = can_callback_without_return_value<functor_t, args_t...>;
    };
}

namespace mixc::interface_can_callback::origin{
    template<class func_t, auto default_return_v = inc::vnull> struct icallback;

    template<class functor_t, class signature_t>
    concept can_callback = meta<functor_t, signature_t>::result == true;

    template<class ret_t, auto default_return_v, class ... args_t>
    struct icallback<ret_t(args_t...), default_return_v>{
    private:
        using func_t                = ret_t(*)(voidp, args_t...);
        using the_t                 = icallback<ret_t(args_t...), default_return_v>;
        mutable voidp   this_ptr    = nullptr;
        mutable func_t  this_func   = nullptr;
    public:
        template<class object_t>
        requires(
            can_callback<object_t, ret_t(args_t...)> or (
                can_callback<object_t, void(args_t...)> and
                inc::is_same<decltype(default_return_v), ret_t>
            )
        )
        constexpr icallback(object_t const & this_ref){
            struct closure{
                static ret_t shell(object_t * this_ptr, args_t ... list){
                    using real_return_t = decltype((*this_ptr)(list...));

                    if constexpr (inc::is_same<void, real_return_t>){
                        (*this_ptr)(list...);

                        if constexpr (not inc::is_same<void, ret_t>){
                            return default_return_v;
                        }
                    }
                    else{
                        return (*this_ptr)(list...);
                    }
                }
            };

            this_ptr    = voidp(xmodify(this_ref));
            this_func   = func_t(xmodify(closure::shell));
        }

        constexpr icallback(decltype(nullptr) = nullptr){}

        ret_t operator()(args_t ... list) const {
            return this_func(this_ptr, list...);
        }

        xis_nullptr(this_func == nullptr)
    };

    template<class ret_t, auto default_return_v, class ... args_t>
    struct icallback<ret_t(*)(args_t...), default_return_v> :
        icallback<ret_t(args_t...), default_return_v>{
        
        using icallback<ret_t(args_t...), default_return_v>::icallback;
    };
}

#endif

xexport_space(mixc::interface_can_callback::origin)